home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994 November: Tool Chest / Dev.CD Nov 94.toast / Sample Code / Macintosh Sample Code / SC.021.ModalList / ModalList.c next >
Encoding:
C/C++ Source or Header  |  1992-06-12  |  17.4 KB  |  587 lines  |  [TEXT/MPS ]

  1. /*
  2. #
  3. #    Apple Macintosh Developer Technical Support
  4. #
  5. #    ModalList : Simple Modal Dialog and List Manager Sample Application
  6. #
  7. #    ModalList.c - Main Segment
  8. #
  9. #    Copyright © 1989 Apple Computer, Inc.
  10. #    All rights reserved.
  11. #
  12. #    Versions:    
  13. #            1.00                     10/89
  14. #            1.01                    06/92
  15. #
  16. #    Components:
  17. #            ModalList.make            October 1, 1989
  18. #            ModalList.h                October 1, 1989
  19. #            ModalList.c                October 1, 1989
  20. #            ModalListInit.c            June 12, 1992
  21. #            ModalList.r                October 1, 1989
  22. #            TCModalList.π            June 12, 1992
  23. #            TCModalList.π.rsrc        June 12, 1992
  24. #
  25. #    ModalList is an example application that demonstrates
  26. #    how to use the Dialog Manager and List Manager routines
  27. #    together. It is not a good example of a sample application
  28. #    but a great example of the use of lists in a dialog. The
  29. #    default LDEF is used to display a 2 dimensional list of strings.
  30. #    Each cell's string is initialized to represent it's position in the
  31. #    list. The user can change these strings and search for a particular
  32. #    setting. Once again this is not meant as an example application and
  33. #    there are some features that are documented in the source listing
  34. #    that you should pay close attention to inorder to understand how
  35. #    this example works.
  36. #
  37. */
  38.  
  39. #pragma segment Main
  40.  
  41. #include <QuickDraw.h>
  42. #include <Fonts.h>
  43. #include <Controls.h>
  44. #include <Dialogs.h>
  45. #include <Windows.h>
  46. #include <TextEdit.h>
  47. #include <Memory.h>
  48. #include <Lists.h>
  49. #include <SegLoad.h>
  50. #include <Packages.h>
  51. #include "ModalList.h"
  52.  
  53. /* Here are the prototypes.
  54. */
  55.  
  56. Boolean                MyList(void);                                /* Modal Dialog loop */
  57.  
  58. pascal void            MyItem(DialogPtr, short);                    /* Dialog Manger call backs */
  59. pascal Boolean        MyFilter(DialogPtr, EventRecord *, short *);
  60.  
  61. pascal Boolean        MyClikLoop(void);                             /* List Manger call backs */
  62. pascal short        MySearch(Ptr, Ptr, short, short);
  63.  
  64. void                LDeselect(ListHandle);                        /* utility routines */
  65. long                Cell2String(Point, char *);
  66.  
  67. /* _DataInit() is part of the MPW runtime library. This external
  68. ** reference to it is done so that its segment can be unloaded, %A5Init.
  69. */
  70. #ifndef THINK_C
  71. extern void _DataInit();
  72. #endif
  73.  
  74. main()
  75. {
  76. #ifndef THINK_C
  77.     UnloadSeg((Ptr) _DataInit);        /* note that _DataInit must not be in Main! */
  78. #endif
  79.     
  80.     MaxApplZone();                    /* expand the heap so code segments load at the top */
  81.  
  82.     Initialize();
  83.     UnloadSeg((Ptr) Initialize);    /* note that Initialize must not be in Main! */
  84.  
  85.     (void) MyList();                /* this sample ignores the return value */
  86. }
  87.  
  88.  
  89. /* a utility routine to deselect all selected cells in the list.
  90. */
  91.  
  92. void LDeselect(ListHandle list)
  93. {
  94.     auto    Point        cell;
  95.     
  96.     /* start with the upper left cell
  97.     */
  98.     SetPt(&cell, 0, 0);
  99.     
  100.     /* for each selected cell
  101.     */
  102.     while(LGetSelect(true, &cell, list))
  103.         
  104.         /* deselect and unhilite it
  105.         */
  106.         LSetSelect(false, cell, list);
  107. }
  108.  
  109. /* a click loop procedure that does nothing but prove it can be done.
  110. */
  111.  
  112. pascal Boolean MyClikLoop(void)
  113. {
  114.     return(true);
  115. }
  116.  
  117. /* a custom search procedure that is case sensitive. the standard list search procedure
  118. ** uses IUMagIDString which is case insensitive.
  119. ** Parameters:    Ptr        aPtr;        the cell data
  120. **                Ptr        bPtr;        the search pattern
  121. **                short    aLen, bLen;    the lengths
  122. */
  123.  
  124. pascal short MySearch(Ptr aPtr, Ptr bPtr, short aLen, short bLen)
  125. {
  126.     return (0 != IUMagString(aPtr, bPtr, aLen, bLen));
  127. }
  128.  
  129. /* this routine will be called by the Dialog Manager to draw the outline of the
  130. ** default button and the list. 
  131. */
  132.  
  133. pascal void MyItem(DialogPtr dialog, short itemNo)
  134. {
  135.     auto    short        itemType;
  136.     auto    Handle        itemHandle;
  137.     auto    Rect        itemRect;
  138.     
  139.     auto    ListHandle    list;
  140.     
  141.     GetDItem(dialog, itemNo, &itemType, &itemHandle, &itemRect);
  142.     switch(itemNo) {
  143.     
  144.     /* outline the default button (see IM I-407).  in this case it is the OK button.
  145.     ** this lets the user know that pressing the return will have the same effect as 
  146.     ** clicking this button.
  147.     */
  148.     case cOKOutlineItem :
  149.         PenSize(3, 3);
  150.         InsetRect(&itemRect, -4, -4);
  151.         FrameRoundRect(&itemRect, 16, 16);
  152.         PenSize(1, 1);
  153.         break;
  154.     
  155.     /* draw the list. the List Manager will draw the cells and scrollbars but does not
  156.     ** draw a border around the list's view rectangle, so it is done here also.
  157.     */
  158.     case cListItem :
  159.  
  160.         /* recover the list handle. it was stuffed into the dialog window's refCon
  161.         ** field when is was created.
  162.         */
  163.         list = (ListHandle) ((DialogPeek)dialog)->window.refCon;
  164.         
  165.         /* let the List Manager draw the list
  166.         */
  167.         LUpdate(dialog->visRgn, list);
  168.  
  169.         /* drawn the lists framing rectangle OUTSIDE the view rectangle.
  170.         ** if the frame is drawn inside the view rectangle then these lines
  171.         ** will be erased, drawn onto or scrolled by the List Manager since the lines
  172.         ** are within the rectangle LM expects to be able to draw in.
  173.         */
  174.         InsetRect(&itemRect, -1, -1);
  175.         FrameRect(&itemRect);
  176.         break;
  177.     }
  178. }
  179.  
  180. /* we need to be able to process mouse clicks in the list. the Dialog Manager makes this 
  181. ** possible through filter procedures like this one. since the default filter procedure
  182. ** will be replaced we also need to handle return key presses.
  183. */
  184.  
  185. pascal Boolean MyFilter(DialogPtr dialog, EventRecord *event, short *itemHit)
  186. {
  187.     auto    ListHandle    list;
  188.     auto    Point        cell;
  189.     auto    char        character;
  190.     auto    Point        where;
  191.     auto    Rect        itemRect;
  192.     auto    short        itemType;
  193.     auto    Handle        itemHandle;
  194.     auto    Str255        string;
  195.     auto    short        length;
  196.  
  197.     switch (event->what) {
  198.     
  199.     /* watch for mouse clicks in the List
  200.     */
  201.     case mouseDown :
  202.         GetDItem(dialog, cListItem, &itemType, &itemHandle, &itemRect);
  203.         where = event->where;
  204.         GlobalToLocal(&where);
  205.         
  206.         /* if the user has clicked in the list then we'll handle the processing here
  207.         */
  208.         if (PtInRect(where, &itemRect)) {
  209.         
  210.             /* recover the list handle. it was stuffed into the dialog window's refCon
  211.             ** field when it was created.
  212.             */
  213.             list = (ListHandle) ((DialogPeek)dialog)->window.refCon;
  214.             
  215.             /* let the List Manager process the mouse down. this includes cell selection
  216.             ** dragging, scrolling and double clicks by the user.
  217.             */
  218.             if (LClick(where, event->modifiers, list)) {
  219.             
  220.                 /* a double click in a cell has occured.
  221.                 ** find out in which one of the cells the user has double clicked in.
  222.                 */
  223.                 cell = LLastClick(list);
  224.                 
  225.                 /* copy this cell's contents to the text edit box in the dialog
  226.                 */
  227.                 GetDItem(dialog, cTextItem, &itemType, &itemHandle, &itemRect);
  228.                 length = 255;
  229.                 LGetCell(&string[1], &length, cell, list);
  230.                 string[0] = (char) length;
  231.                 SetIText(itemHandle, string);
  232.             }
  233.             
  234.             /* tell the application that the list has been clicked in.
  235.             */
  236.             *itemHit = cListItem;
  237.             
  238.             /* tell the Dialog Manager that the event has been handled.
  239.             */
  240.             return true;
  241.         }
  242.         break;
  243.     
  244.     /* be sure and return this information so the Dialog Manager will process the 
  245.     ** return and enter key presses as clicks by the user in the OK button. this is
  246.     ** only required because we have overridden the Dialog Manager's default filtering.
  247.     */
  248.     case keyDown :    
  249.     case autoKey :
  250.         character = event->message;
  251.         if ('\n' == character) {
  252.  
  253.             /* tell the application that the OK button has been clicked by the user.
  254.             */
  255.             *itemHit = ok;
  256.             
  257.             /* tell the Dialog Manger that the event has been handled and the
  258.             ** character should not be added to the text edit field.
  259.             */
  260.             return true;
  261.         }
  262.         break;
  263.     }
  264.     
  265.     /* tell the Dialog Manger that the event has NOT been handled and that it should
  266.     ** take further action on this event.
  267.     */
  268.     return false;
  269. }
  270.  
  271. /* this routine will not return until the user has dismissed the modal dialog with
  272. ** a press of the return key or a click in the ok or cancel buttons. it displays a 
  273. ** list, check boxes for setting selection flags, a text edit field for searching and setting
  274. ** cell data and the usual ok and cancel buttons. the list uses the default LDEF procedure.
  275. **
  276. ** if you double click in a cell the cells contents will be moved to the
  277. ** text edit field. edit the field and click the set button and you will have edited the
  278. ** cell contents.
  279. **
  280. ** note that the Set button sets the last cell clicked in to the contents of the text edit field.
  281. ** it does not necessarily set the cell which is selected or hilited but it sets the cell that
  282. ** LLastClick returns. it is sort of a funky user interface but it does demonstrate the
  283. ** use of LLastClick.
  284. **
  285. ** if you scroll the list all the way to the right you will notice that there is some extra
  286. ** white space to the right of the right most cell. this was done intentionally to show what
  287. ** can happen when the lists viewing rectangle is not an even multiple of the cell rectangles.
  288. ** the List Manager must scroll this extra bit so that it can display the entire width of the
  289. ** right most cells. notice too that the bottom most cell does not display this behavior since
  290. ** the height of the list's viewing rectangle is an even multiple of the cell height.
  291. **
  292. ** note that a call to LSetSelect will select or hilite a cell which is empty even though 
  293. ** the list's selection flags field is set to lNoNilHilite. the selection flags
  294. ** are used only by the LClick routine they do not prevent the program from selecting 
  295. ** cells with LSetSelect in such a way that may seem inconsistent with their
  296. ** setting. this peculiarity can also happen with other selection flag settings and the
  297. ** LSetSelect call.
  298. **
  299. ** notice also that the column widths are all equal, there is no way to make cells with different 
  300. ** column widths using the List Manager.
  301. **
  302. ** there have been requests for the capability to have multiple lists displayed in a
  303. ** window and have them both scrolled by one scrollbar. the only way to accomplish this 
  304. ** would be to make new lists without the List Manager's scrollV or scrollH scrollbars.
  305. ** then create a new scrollbar using the Control Manager and setup an action procedure for
  306. ** TrackControl which calls LScroll for each of the lists. this is not demonstrated in
  307. ** this sample program.
  308. **
  309. */
  310.  
  311. Boolean MyList(void)
  312. {
  313.     auto    DialogPtr    dialog;            /* a dialog containing the list item */
  314.  
  315.     auto    short        itemNo;            /* the item in the dialog selected */
  316.     auto    short        itemType;        /* dummy parameter for call to GetDItem */
  317.     auto    Handle        itemHandle;        /* dummy parameter for call to GetDItem */
  318.     auto    Rect        itemRect;         /* the location of the list in the dialog */
  319.     
  320.     auto    ListHandle    list;            /* the list constructed in the dialog */
  321.     auto    Rect        dataBounds;        /* the dimensions of the data in the list */
  322.     auto    Point        cellSize;        /* width and height of a cells rectangle */
  323.     auto    Point        cell;            /* an index through the list */
  324.  
  325.     auto    char        string[255];
  326.     auto    short        length;
  327.     
  328.     auto    short        checked;        /* flag for setting and getting check box value */
  329.     auto    short        bit;            /* used as a mask to test the selection flags */ 
  330.     
  331.     /* create a new dialog window
  332.     */
  333.     dialog = GetNewDialog(cDLOGID, (Ptr) 0, (WindowPtr) -1);
  334.     SetPort((GrafPtr) dialog);
  335.         
  336.     /* set the procedure pointer for the user items in the dialog.
  337.     ** this will allow he default button to be outlined and the list to be drawn
  338.     ** by the Dialog Manger.
  339.     */
  340.     GetDItem(dialog, cOKOutlineItem, &itemType, &itemHandle, &itemRect);
  341.     SetDItem(dialog, cOKOutlineItem, itemType, (Handle) MyItem, &itemRect);
  342.     
  343.     GetDItem(dialog, cListItem, &itemType, &itemHandle, &itemRect);
  344.     SetDItem(dialog, cListItem, itemType, (Handle) MyItem, &itemRect);
  345.  
  346.     /* make room for scroll bars (see IM IV-270)
  347.     */
  348.      itemRect.right -= 15;
  349.      itemRect.bottom -= 15;
  350.  
  351.     /* create a list
  352.     */
  353.     SetRect(&dataBounds, 0, 0, cListCols, cListRows);
  354.     SetPt(&cellSize, cListCellWidth, cListCellHeight);
  355.     list = LNew(&itemRect, &dataBounds, cellSize, 0, (WindowPtr) dialog, false, false, true, true);
  356.  
  357.     /* allow the dialog manager routines to access the list record
  358.     */
  359.     ((DialogPeek)dialog)->window.refCon = (long) list;
  360.     
  361.     /* use the custom click loop procedure
  362.     */
  363.     (*list)->lClikLoop = (ProcPtr) MyClikLoop;
  364.  
  365.     /* use the default selection flags
  366.     */
  367.     (*list)->selFlags = 0;
  368.     
  369.     /* initialize the check boxes in the dialog according
  370.     ** to the settings of the list's selection flags
  371.     */
  372.     bit = 2;
  373.     itemNo = cNoNilHiliteItem;
  374.     while (itemNo <= cOnlyOneItem) {
  375.     
  376.         GetDItem(dialog, itemNo, &itemType, &itemHandle, &itemRect);
  377.          checked = ((*list)->selFlags == ((*list)->selFlags | bit)) ? 1 : 0;
  378.          SetCtlValue((ControlHandle) itemHandle, checked);
  379.         
  380.         bit *= 2;
  381.         ++itemNo;
  382.        }
  383.  
  384.     /* initialize the cell's contents.
  385.     */
  386.     for (cell.v = 0; cell.v < (**list).dataBounds.bottom; ++cell.v) {
  387.         for (cell.h = 0; cell.h < (**list).dataBounds.right; ++cell.h) {
  388.         
  389.             /* make a string representing the cell's position in the list.
  390.             */
  391.             length = Cell2String(cell, string);
  392.             
  393.             /* initialize the cell contents to this string.
  394.             **
  395.             ** you would initialize your cell data with what ever is appropriate
  396.             ** for your application here.
  397.             */
  398.             LSetCell(string, length, cell, list);
  399.         }
  400.     }
  401.     
  402.     /* turn cell drawing on only after the cell contents have been initialized.
  403.     ** this will avoid watching the delay between the LSetCells calls and is faster.
  404.     */
  405.     LDoDraw(true, list);
  406.     
  407.     do {
  408.     
  409.         /* process hits in the dialog.
  410.         */
  411.         ModalDialog(MyFilter, &itemNo);
  412.         
  413.         switch(itemNo) {
  414.  
  415.         /* process hits in the OK button.
  416.         */ 
  417.         case ok :
  418.         
  419.             /* find out which cells have been selected.
  420.             */
  421.             SetPt(&cell, 0, 0);
  422.             while(LGetSelect(true, &cell, list)) {
  423.             
  424.                 /* there is nothing to do with the user's selections in this sample
  425.                 ** so i'll just deselect the cells the users has selected.
  426.                 */
  427.                 LSetSelect(false, cell, list);
  428.             }
  429.             break;
  430.         
  431.         /* process hits in the Find button.
  432.         */ 
  433.         case cFindItem :
  434.         
  435.             GetDItem(dialog, cTextItem, &itemType, &itemHandle, &itemRect);
  436.             GetIText(itemHandle, string);
  437.             
  438.             /* search for a match in the list.
  439.             ** start the search at the upper left most cell in the list.
  440.             **
  441.             ** the List Manger performs the searchs row by row until it finds
  442.             ** the first match. this order can not be changed without writing a
  443.             ** replacement to LSearch, probably making use of LGetCell, LNextCell and
  444.             ** the International search routines.
  445.             */
  446.             SetPt(&cell, 0, 0);
  447.             
  448.             /* deselect all cells which are currently selected.
  449.             */
  450.             LDeselect(list);
  451.                 
  452.             /* use the custom search procedure.
  453.             ** the default search procedure is not case sensitive.
  454.             */
  455.             if (LSearch(&string[1], string[0], MySearch, &cell, list)) {
  456.             
  457.                 /* hilite the cell which contains the matching string.
  458.                 **
  459.                 ** note that this call will select or hilite a cell which is empty
  460.                 ** when the users search string is empty even though the list's
  461.                 ** selection flags field is set to lNoNilHilite. the selection flags
  462.                 ** are used only by the LClick routine they do not prevent the program
  463.                 ** from selecting cells with LSetSelect in such a way that may seem 
  464.                 ** inconsistent with their setting. this peculiarity can also happen
  465.                 ** with other selection flag settings and the LSetSelect call.
  466.                 */
  467.                 LSetSelect(true, cell, list);
  468.                 
  469.                 /* if the matching cell is not visible it will be scrolled into view.
  470.                 */
  471.                 LAutoScroll(list);
  472.             }
  473.             break;
  474.             
  475.         /* process hits in the Set button.
  476.         */ 
  477.         case cSetItem :
  478.             GetDItem(dialog, cTextItem, &itemType, &itemHandle, &itemRect);
  479.             GetIText(itemHandle, string);
  480.             
  481.             /* find out which was the last cell clicked in by the user.
  482.             ** not the last cell dragged into, or the last cell selected but the
  483.             ** last cell a mouse down event occured in.
  484.             */
  485.             cell = LLastClick(list);
  486.             if (0 <= cell.h && 0 <= cell.v) {
  487.             
  488.                 
  489.                 /* if a cell has been clicked in then deselect all cells which are
  490.                 ** currently selected.
  491.                 */
  492.                 LDeselect(list);
  493.  
  494.                 /* set the cell to the string the user has entered.
  495.                 */
  496.                 LSetCell(&string[1], string[0], cell, list);
  497.                 
  498.                 /* notice that this will Hilite an empty cell even with
  499.                 ** the list's selection flags set to lNoNilHilite.
  500.                 */
  501.                 LSetSelect(true, cell, list);
  502.  
  503.                 /* if the last cell clicked in is not visible it will be scrolled
  504.                 ** into view.
  505.                 */
  506.                 LAutoScroll(list);
  507.             }
  508.             break;
  509.             
  510.         /* process hits in the check boxes representing the list's selection flags.
  511.         */ 
  512.         case cOnlyOneItem :
  513.         case cExtendDragItem :
  514.         case cNoDisjointItem :
  515.         case cNoExtendItem :
  516.         case cNoRectItem :
  517.         case cUseSenseItem :
  518.         case cNoNilHiliteItem :
  519.         
  520.             /* deselect all cells which are currently selected.
  521.             */
  522.             LDeselect(list);
  523.             
  524.             /* toggle the check box.
  525.             */
  526.             GetDItem(dialog, itemNo, &itemType, &itemHandle, &itemRect);
  527.               checked = GetCtlValue((ControlHandle) itemHandle);
  528.               SetCtlValue((ControlHandle) itemHandle, !checked);
  529.  
  530.             /* adjust the selection flags to the users new settings.
  531.             */
  532.             switch (itemNo) {
  533.             case cOnlyOneItem :
  534.                 (*list)->selFlags ^= lOnlyOne; break;
  535.             case cExtendDragItem :
  536.                 (*list)->selFlags ^= lExtendDrag; break;
  537.             case cNoDisjointItem :
  538.                 (*list)->selFlags ^= lNoDisjoint; break;
  539.             case cNoExtendItem :
  540.                 (*list)->selFlags ^= lNoExtend; break;
  541.             case cNoRectItem :
  542.                 (*list)->selFlags ^= lNoRect; break;
  543.             case cUseSenseItem :
  544.                 (*list)->selFlags ^= lUseSense; break;
  545.             case cNoNilHiliteItem :
  546.                 (*list)->selFlags ^= lNoNilHilite; break;
  547.             }
  548.  
  549.             break;
  550.         }                
  551.     } while ((itemNo != ok) && (itemNo != cancel));
  552.     
  553.     /*  kill the list and dialog.
  554.     */
  555.     LDispose(list);
  556.     DisposDialog(dialog);
  557.     
  558.     /* return true if OK or RETURN key was hit.
  559.     */
  560.     return (ok == itemNo);
  561. }
  562.  
  563. /* just a funky routine to create strings for the cell's data
  564. */
  565.  
  566. long Cell2String(Point pt, char *string)
  567. {
  568.     auto    short        index;
  569.     auto    Str255        rowStr;
  570.     auto    Str255        colStr;
  571.     
  572.     NumToString((long)pt.v, rowStr);
  573.     *(string)++ = 'R';
  574.     for (index = 1; index <= rowStr[0]; ++index)
  575.         *(string++) = rowStr[index];
  576.         
  577.     *(string)++ = ',';
  578.     *(string)++ = ' ';
  579.  
  580.     NumToString((long)pt.h, colStr);
  581.     *(string)++ = 'C';
  582.     for (index = 1; index <= colStr[0]; ++index) 
  583.         *(string++) = colStr[index];
  584.     
  585.     return colStr[0] + rowStr[0] + 4;
  586. }
  587.